<script setup lang="ts">
import { DownOutlined, QuestionCircleOutlined, UpOutlined } from '@vicons/antd';
import type { GridProps } from 'naive-ui/lib/grid';
import { isArray, isFunction } from 'lodash-es';

import type { FormActionType, FormSchema } from './types/form';

import type { BasicFormProps } from '@/components/form/src/props';
import { createPlaceholderMessage } from '@/components/form/src/helpers/placeholder';
import { formFieldComponentMap } from '@/components/form/src/component-map';
import { useFormValues } from '@/components/form/src/hooks/use-form-values';
import { useFormEvents } from '@/components/form/src/hooks/use-form-events';

const props = withDefaults(defineProps<BasicFormProps>(), {
  labelWidth: 120,
  inline: false,
  layout: 'horizontal',
  size: 'medium',
  labelPlacement: 'left',
  isFull: true,
  showActionButtonGroup: false,
  showResetButton: false,
  resetButtonText: '重置',
  showSubmitButton: false,
  submitButtonText: '查询',
  showAdvancedButton: false,
  collapsed: false,
  collapsedRows: 1,
});

const emits = defineEmits< {
  (e: 'submit', formModel: Recordable): void;
  (e: 'reset', formModel: Recordable): void;
  (e: 'register', formAction: Partial<FormActionType>): void;
  (e: 'submit-success', formModel: Recordable): void;
  (e: 'submit-failed', formModel: Recordable): void;
  (e: 'submit-finished', formModel: Recordable): void;
}>();

const attrs = useAttrs();

const defaultFormModel = ref<Recordable>({});
const formModel = reactive<Recordable>({});
const propsRef = ref<Partial<BasicFormProps>>({});
const formElRef = ref<Nullable<FormActionType>>(null);
const isUpdateDefaultRef = ref(false);

const submitLoading = ref(false);

const getProps = computed((): BasicFormProps => {
  const formProps = { ...props, ...unref(propsRef) } as BasicFormProps;
  const rulesObj: any = {
    rules: {},
  };
  const schemas = formProps.schemas || [];
  schemas.forEach(item => {
    if (item.rules && isArray(item.rules)) {
      rulesObj.rules[item.field] = item.rules;
    }
  });
  return { ...formProps, ...unref(rulesObj) };
});

const getBindValue = computed(
  () => ({ ...attrs, ...props, ...unref(getProps) } as Recordable),
);

/**
 * 展开收起
 */
const gridCollapsed = ref(true);
function unfoldToggle() {
  gridCollapsed.value = !gridCollapsed.value;
}

/**
 * 布局
 */
const isInline = computed(() => {
  const { layout } = unref(getProps);
  return layout === 'inline';
});
const getGrid = computed((): GridProps => {
  const { gridProps } = unref(getProps);
  return {
    ...gridProps,
    collapsed: isInline.value ? gridCollapsed.value : false,
    responsive: 'screen',
  };
});
const getSubmitBtnOptions = computed(() => {
  return {
    size: props.size,
    type: 'primary',
    ...props.submitButtonOptions,
  };
});
const getResetBtnOptions = computed(() => {
  return {
    size: props.size,
    type: 'default',
    ...props.resetButtonOptions,
  };
});

/**
 * 表单项schema
 */
const schemaRef = ref<Nullable<FormSchema[]>>(null);
const getSchema = computed((): FormSchema[] => {
  let schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
  schemas = schemas
    .map(schema => {
      let display = schema.display;
      if (isFunction(display)) {
        display = display.call(undefined, formModel);
      }

      return {
        ...schema,
        display,
        defaultValue: schema.defaultValue ?? null,
      };
    });
  return schemas;
});
const visibleSchemas = computed(() => {
  const schemas = unref(getSchema);
  return schemas.filter(schema => schema.display ?? true);
});

/**
 * 表单值
 */
const { initDefault } = useFormValues({
  defaultFormModel,
  getSchema,
  formModel,
});
watch(
  () => getSchema.value,
  schema => {
    if (unref(isUpdateDefaultRef)) {
      return;
    }
    if (schema?.length) {
      initDefault();
      isUpdateDefaultRef.value = true;
    }
  },
);

/**
 * 表单方法
 */
const {
  handleSubmit,
  validate,
  resetFields,
  getFieldsValue,
  clearValidate,
  setFieldsValue,
} = useFormEvents({
  emits,
  getProps,
  getSchema,
  formElRef: formElRef as Ref<FormActionType>,
  formModel,
  defaultFormModel,
  submitLoading,
});
const formActionType: Partial<FormActionType> = {
  getFieldsValue,
  setFieldsValue,
  resetFields,
  validate,
  clearValidate,
  submit: handleSubmit,
};
defineExpose(formActionType);

/**
 * 表单项组件
 */
function getComponentProps(schema: FormSchema) {
  const compProps: Recordable = schema.componentProps ?? {};
  const placeholder = compProps?.placeholder ?? createPlaceholderMessage(schema);

  return {
    clearable: true,
    placeholder,
    ...compProps,
  };
}

onMounted(() => {
  initDefault();
  emits('register', formActionType);
});
</script>

<template>
  <NForm ref="formElRef" v-bind="getBindValue" :model="formModel">
    <NGrid v-bind="getGrid">
      <NGi v-for="schema of visibleSchemas" v-bind="schema.giProps" :key="schema.field">
        <NFormItem :label="schema.label" :path="schema.field">
          <!-- 标签名右侧温馨提示 -->
          <template v-if="schema.labelMessage" #label>
            {{ schema.label }}
            <NTooltip trigger="hover" :style="schema.labelMessageStyle">
              <template #trigger>
                <NIcon size="18" class="text-gray-400 cursor-pointer">
                  <QuestionCircleOutlined />
                </NIcon>
              </template>
              {{ schema.labelMessage }}
            </NTooltip>
          </template>

          <!-- 动态渲染表单组件 -->
          <component
            v-bind="getComponentProps(schema)"
            :is="formFieldComponentMap[schema.component]"
            v-model:value="formModel[schema.field]"
          />
        </NFormItem>
      </NGi>

      <!-- 提交 重置 展开 收起 按钮 -->
      <NGi
        v-if="getProps.showActionButtonGroup"
        :span="isInline ? '' : 24"
        :suffix="!!isInline"
        #="{ overflow }"
      >
        <NSpace
          align="center"
          :justify="isInline ? 'end' : 'start'"
        >
          <NButton
            v-if="getProps.showSubmitButton"
            v-bind="getSubmitBtnOptions"
            @click="handleSubmit"
          >
            {{ getProps.submitButtonText }}
          </NButton>
          <NButton
            v-if="getProps.showResetButton"
            v-bind="getResetBtnOptions"
            @click="resetFields"
          >
            {{ getProps.resetButtonText }}
          </NButton>
          <NButton
            v-if="isInline && getProps.showAdvancedButton"
            type="primary"
            text
            icon-placement="right"
            @click="unfoldToggle"
          >
            <template #icon>
              <NIcon v-if="overflow" size="14" class="unfold-icon">
                <DownOutlined />
              </NIcon>
              <NIcon v-else size="14" class="unfold-icon">
                <UpOutlined />
              </NIcon>
            </template>
            {{ overflow ? '展开' : '收起' }}
          </NButton>
        </NSpace>
      </NGi>
    </ngrid>
  </NForm>
</template>
